package com.coderforum.community.service;

import com.coderforum.community.dto.CommentDTO;
import com.coderforum.community.enums.CommentTypeEnum;
import com.coderforum.community.enums.NotificationStatusEnum;
import com.coderforum.community.enums.NotificationTypeEnum;
import com.coderforum.community.exception.CustomizeErrorCode;
import com.coderforum.community.exception.CustomizeException;
import com.coderforum.community.mapper.*;
import com.coderforum.community.model.*;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 提供向数据库中插入评论的服务，同时提供评论数量更新的服务
 *
 * @Author: xuexuehai
 * @MailBox: xuehai.xue@qq.com
 * @Date: 2021/4/27 8:44 下午
 */
@Service
public class CommentService {

    @Autowired
    private CommentMapper commentMapper;

    @Autowired
    private QuestionMapper questionMapper;

    @Autowired
    private QuestionExtMapper questionExtMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private CommentExtMapper commentExtMapper;

    @Autowired
    private NotificationMapper notificationMapper;

    /**
     * 向数据库插入评论
     *
     * @param comment 传入的评论对象
     * @param commentator 传入的评论者
     */
    @Transactional
    public void insert(Comment comment, User commentator) {
        //当指定问题不存在时，抛出指定问题不存在不可评论的异常
        if (comment.getParentId() == null || comment.getParentId() == 0) {
            throw new CustomizeException(CustomizeErrorCode.TARGET_PARAM_NOT_FOUND);
        }
        //类型评论错误，或不存在
        if (comment.getType() == null || !CommentTypeEnum.isExist(comment.getType())) {
            throw new CustomizeException(CustomizeErrorCode.TYPE_PARAM_WRONG);
        }

        //通过上面两个if的判断，此时评论一定是属于问题或者评论的评论，因此使用下面的if进行区分
        if (comment.getType().equals(CommentTypeEnum.COMMENT.getType())) {
            //回复评论的评论
            Comment dbComment = commentMapper.selectByPrimaryKey(comment.getParentId());
            //如果被回复的评论不存在，则抛出异常，代码为COMMENT_NOT_FOUND
            if (dbComment == null) {
                throw new CustomizeException(CustomizeErrorCode.COMMENT_NOT_FOUND);
            }

            //回复问题的评论
            Question question = questionMapper.selectByPrimaryKey(dbComment.getParentId());
            //如果被回复的问题不存在，则抛出异常，代码为QUESTION_NOT_FOUND
            if (question == null) {
                throw new CustomizeException(CustomizeErrorCode.QUESTION_NOT_FOUND);
            }

            //如果存在则向数据库中插入此评论
            commentMapper.insert(comment);

            //增加评论数
            Comment parentComment = new Comment();
            parentComment.setId(comment.getParentId());
            parentComment.setCommentCount(1L);
            commentExtMapper.incCommentCount(parentComment);

            //创建对评论回复的通知
            createNotify(comment, dbComment.getCommentator(), commentator.getName(), question.getTitle(), NotificationTypeEnum.REPLY_COMMENT, question.getId());
        } else {
            //回复问题的评论
            Question question = questionMapper.selectByPrimaryKey(comment.getParentId());
            //如果被回复的问题不存在，则抛出异常，代码为QUESTION_NOT_FOUND
            if (question == null) {
                throw new CustomizeException(CustomizeErrorCode.QUESTION_NOT_FOUND);
            }
            //如果存在则向数据库中插入此评论
            commentMapper.insert(comment);
            //将属于Question对象的question的评论数设置成 1
            question.setCommentCount(1);
            /* 使用非自动生成的Mapper增加 1，其中原理是数据库中的评论数加上上述的question评论数即 1，
               作为新值赋给数据库中的评论数*/
            questionExtMapper.incCommentCount(question);

            //创建对问题回复的通知
            createNotify(comment,question.getCreator() ,commentator.getName(),question.getTitle(), NotificationTypeEnum.REPLY_QUESTION, question.getId());
        }
    }

    /**
     * 创建回复的通知
     * @param comment 通知所属的评论
     * @param receiver 通知的接收者
     * @param notifierName 通知的接收者的名称
     * @param outerTitle 通知的内容（文章的标题）
     * @param notificationType 通知的类型（是针对问题的回复还是针对评论的回复）
     * @param outerId 评论回复的id
     */
    private void createNotify(Comment comment, Long receiver, String notifierName, String outerTitle, NotificationTypeEnum notificationType, Long outerId) {
        if(receiver.equals(comment.getCommentator())){
            return;
        }
        Notification notification = new Notification();
        notification.setGmtCreate(System.currentTimeMillis());
        notification.setType(notificationType.getType());
        notification.setOuterId(outerId);
        notification.setNotifier(comment.getCommentator());
        notification.setStatus(NotificationStatusEnum.UNREAD.getStatus());
        notification.setReceiver(receiver);
        notification.setNotifierName(notifierName);
        notification.setOuterTitle(outerTitle);
        notificationMapper.insert(notification);
    }

    /**
     * 实现在问题详情页展示评论列表的功能
     * @param id 某个要展示的问题评论的ID
     * @param type
     * @return 返回一个CommentDTOList对象
     */
    public List<CommentDTO> listByTargetId(Long id, CommentTypeEnum type) {
        CommentExample commentExample = new CommentExample();
        commentExample.createCriteria()
                .andParentIdEqualTo(id)
                .andTypeEqualTo(type.getType());
        //按照创建时间的倒叙排序
        commentExample.setOrderByClause("gmt_create desc");
        List<Comment> comments = commentMapper.selectByExample(commentExample);

        //jdk1.8 之后支持的语法
        if (comments.size() == 0) {
            return new ArrayList<>();
        }
        //使用拉姆达获取去重后的评论人
        Set<Long> commentators = comments.stream().map(comment -> comment.getCommentator()).collect(Collectors.toSet());
        List<Long> userIds = new ArrayList();
        userIds.addAll(commentators);

        //获取评论人并转化为 Map，转换为 Map 就可以一次获取到，降低时间复杂度
        UserExample userExample = new UserExample();
        userExample.createCriteria()
                .andIdIn(userIds);
        List<User> users = userMapper.selectByExample(userExample);
        Map<Long, User> userMap = users.stream().collect(Collectors.toMap(user -> user.getId(), user -> user));

        //转换 comment 为 commentDTO
        List<CommentDTO> commentDTOList = comments.stream().map(comment -> {
            CommentDTO commentDTO = new CommentDTO();
            BeanUtils.copyProperties(comment, commentDTO);
            commentDTO.setUser(userMap.get(comment.getCommentator()));

            return commentDTO;
        }).collect(Collectors.toList());

        return commentDTOList;
    }
}
